package com.wangshouyu.plugins.swagger;


import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.ClassUtils;
import springfox.documentation.RequestHandler;

import java.lang.annotation.Annotation;

/**
 * copy from {@link springfox.documentation.builders.RequestHandlerSelectors}
 * @author 王守钰
 * @date 2022年01月06日 20:29
 * @description 匹配处理
 */
public class MatchRequestHandlerSelectors {

    private MatchRequestHandlerSelectors() {
        throw new UnsupportedOperationException();
    }

    /**
     * Any RequestHandler satisfies this condition
     *
     * @return predicate that is always true
     */
    public static Predicate<RequestHandler> any() {
        return Predicates.alwaysTrue();
    }

    /**
     * No RequestHandler satisfies this condition
     *
     * @return predicate that is always false
     */
    public static Predicate<RequestHandler> none() {
        return Predicates.alwaysFalse();
    }

    /**
     * Predicate that matches RequestHandler with handlers methods annotated with given annotation
     *
     * @param annotation - annotation to check
     * @return this
     */
    public static Predicate<RequestHandler> withMethodAnnotation(final Class<? extends Annotation> annotation) {
        return new Predicate<RequestHandler>() {
            @Override
            public boolean apply(RequestHandler input) {
                return input.isAnnotatedWith(annotation);
            }
        };
    }

    /**
     * Predicate that matches RequestHandler with given annotation on the declaring class of the handler method
     *
     * @param annotation - annotation to check
     * @return this
     */
    public static Predicate<RequestHandler> withClassAnnotation(final Class<? extends Annotation> annotation) {
        return new Predicate<RequestHandler>() {
            @Override
            public boolean apply(RequestHandler input) {
                return declaringClass(input).transform(annotationPresent(annotation)).or(false);
            }
        };
    }

    private static Function<Class<?>, Boolean> annotationPresent(final Class<? extends Annotation> annotation) {
        return new Function<Class<?>, Boolean>() {
            @Override
            public Boolean apply(Class<?> input) {
                return input.isAnnotationPresent(annotation);
            }
        };
    }


    private static Function<Class<?>, Boolean> handlerPackage(final String basePackage) {
        return new Function<Class<?>, Boolean>() {
            @Override
            public Boolean apply(Class<?> input) {
                return ClassUtils.getPackageName(input).startsWith(basePackage);
            }
        };
    }

    private static Function<Class<?>, Boolean> handlerMatchPackage(final String matchPackage) {
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        return new Function<Class<?>, Boolean>() {
            @Override
            public Boolean apply(Class<?> input) {
                return antPathMatcher.match(matchPackage, ClassUtils.getPackageName(input));
            }
        };
    }

    /**
     * Predicate that matches RequestHandler with given base package name for the class of the handler method.
     * This predicate includes all request handlers matching the provided basePackage
     *
     * @param basePackage - base package of the classes
     * @return this
     */
    public static Predicate<RequestHandler> basePackage(final String basePackage) {
        return new Predicate<RequestHandler>() {
            @Override
            public boolean apply(RequestHandler input) {
                return declaringClass(input).transform(handlerPackage(basePackage)).or(true);
            }
        };
    }

    /**
     * Predicate that matches RequestHandler with given base package name for the class of the handler method.
     * This predicate includes all request handlers matching the provided matchPackage
     *
     * i.e.
     * <em>matchPackage=com.wsy.*.controller</em>
     * <em>myClass=com.wsy.crm.controller.pc</em>
     * myClass will be match matchPackage
     *
     * @param matchPackage - match package of the class
     * @return this
     */
    public static Predicate<RequestHandler> matchPackage(final String matchPackage) {
        return new Predicate<RequestHandler>() {
            @Override
            public boolean apply(RequestHandler input) {
                return declaringClass(input).transform(handlerMatchPackage(matchPackage)).or(true);
            }
        };
    }

    private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
        return Optional.fromNullable(input.declaringClass());
    }
}
